Using Python for Android and QPython




Do you want to teach your Android device some clever tricks without mastering the art of native app development? Install Python, and you can automate practically any task and write complete apps using this popular scripting language.

To make it easier to run various scripting languages on the Android platform, Google created the Scripting Layer for Android (SL4A) project [1]. Although Google at some point has lost interest in maintaining the project, the last release of the framework runs perfectly well on any Android version before 5.0. The Python for Android component includes Python 2.6, so practically any script compatible with this version of Python will work on Android. More importantly, the combination of the scripting layer and Python for Android can be used to access Android APIs, so you can write Python scripts that offer the same functionality as native Android apps.

That's all fine and dandy, but it looks like the SL4A project is no longer maintained, and the available APK packages don't run well on Android 5.0 and higher. Thankfully, the project's code is released under the Apache License 2.0, so the SL4A [2] and Python for Android [3] parts have already been forked and updated to work with Android 5.0.

The SL4A and Python for Android pair is not the only option for using Python for Android. The QPython project [4] aims to build an entire Python environment for the Android platform (Figure 1). QPython is available in Google Play Store in two editions: QPython is based on the Python 2.x series [5], and QPython3 comes with Python 3.x [6]. Both versions are packed with useful features. What's more, QPython supports SL4A, so it provides a drop-in replacement for Google's Scripting Layer for Android and Python for Android. The environment comes with the PIP package manager, which allows you to add practically any Python library and module to your project.


Figure 1: QPython provides a complete environment for writing and running Python scripts.

Speaking of libraries, QPython comes with a handful of Python modules, including Bottle. This means that you can use QPython not only to write simple scripts that run locally but also to develop and deploy Python-based web apps. QPython is bundled with the QEdit editor suitable for writing and testing Python scripts (Figure 2).


Figure 2: QPython comes with the QEdit programming editor.

In short, QPython can transform an Android device into a capable platform for developing and deploying Python scripts. The only fly in the ointment is the fact that QPython contains proprietary components, but its developers promise gradually to open source the entire stack.

Getting Started with QPython

QPython is available as an APK package in Google Play Store, so deploying it on the Android device is as easy as it gets. The initial screen lets you quickly run existing scripts and projects, whereas the main interface gives you access to QPython's modules and tools. The Console shortcut launches an interactive Python console, where you can execute statements individually.

The Programs shortcut opens a simple tool that allows you to browse and manage existing Python scripts and projects. When you tap on a script or a project folder, you have the option to run it, open it for editing, rename it , or delete it. To add Python scripts and projects to QPython, move them into the appropriate folders in the com.hipipal.qpyplus directory on the internal storage of the Android device.

QPython comes with several useful Python libraries preinstalled, including PIL, Pygame, Bottle, BeautifulSoup, and others. You can also install additional libraries using the option in the Libraries section. Here, you can use either QPython's own QPyPI package manager or PIP.

From Python for Android to QPython

If you already have scripts written for SL4A and Python for Android, you'll be pleased to learn that they can be migrated to QPython with a minimum of tweaking. You only need to add a QPython-specific preamble and use the androidhelper library instead of android.

Because QPython provides support for SL4A, you can use the framework to write and run Python scripts that can access and control Android functionality. QPython comes with a couple of ready-made scripts that you can run and study, but the best way to learn the ropes is to write your own scripts that solve real-world problems.

For example, if you often need to look up word definitions, you can whip up a simple Python script that can be used to look up the specified word or term in an online reference of your choice (Listing 1).

Listing 1

Lookup Script

01 #-*-coding:utf8;-*-
02 #qpy:console
03 #qpy:2
04 import androidhelper
05 droid = androidhelper.Android()
06 s = droid.dialogGetInput('Define', 'Word to define:').result
07 droid.startActivity('android.intent.action.VIEW', 'http://www.google.com/search?q=define:' + s)

All scripts in QPython must have a preamble, defining encoding, running mode, and Python version. In Listing 1, the encoding is set to UTF-8, and the script is instructed to run in console mode using Python 2.

Support for SL4A in QPython is provided through the androidhelper library, so the script starts by importing the library and creating the droid object. Android functionality in SL4A can be accessed through so-called facades that are basically friendly wrappers for Android APIs. The lookup script uses the dialogGetInput UI facade to display an input box and obtain the entered value. Once the script has a word or term to look up, it uses the android.intent.action.VIEW intent to open the provided URL (in this case, it's a Google search query followed by the obtained word or term) in the default browser.

SL4A provides a large collection of facades, and the project's wiki [7] offers an overview of all available facades along with their brief descriptions.

To run the script, move it to the com.hipipal.qpyplus/scripts folder, launch QPython, tap the Quick Launch button, choose Run local script, and select the script. For faster access, switch to the main interface, tap the Programs shortcut, and long-tap on the script to create a shortcut on the home screen.

From Android to Mejiro

Although plenty of services are available for sharing photos, I use Mejiro [8], a single-file photo-publishing PHP web app. This no-frills app allows me to publish photos by simply uploading them to the dedicated photos directory. To make the entire process of snapping a photo and uploading it to Mejiro more straightforward, I wrote a relatively simple Python script that takes care of that (Listing 2).

Listing 2

Upload to Mejiro Script

01 #-*-coding:utf8;-*-
02 #qpy:console
03 #qpy:2
04 import androidhelper, time, os, ftplib
05 # Specify FTP connection info
06 server = '127.0.0.1'
07 username = 'username'
08 password = 'password'
09 droid = androidhelper.Android()
10 mejiro_dir = '/sdcard/mejiro/'
11 if not os.path.exists(mejiro_dir):
12 os.makedirs(mejiro_dir)
13 timestamp = time.strftime('%Y%m%d-%H%M%S', time.localtime())
14 droid.cameraInteractiveCapturePicture(mejiro_dir + timestamp + '.jpg')
15 description_text = droid.dialogGetInput('Description', 'Add a description to the photo:').result
16 description_file = timestamp + '.txt'
17 file = open(mejiro_dir + description_file, 'a')
18 file.write('%s\n' % (description_text))
19 file.close()
20 droid.dialogCreateAlert('Upload the photo and the description file?')
21 droid.dialogSetPositiveButtonText('Yes')
22 droid.dialogSetNegativeButtonText('No')
23 droid.dialogShow()
24 response = droid.dialogGetResponse().result
25 if response['which'] == 'positive':
26 droid.dialogCreateSpinnerProgress('Uploading to FTP...')
27 droid.dialogShow()
28 conn = ftplib.FTP(server, username, password)
29 file = open (mejiro_dir + timestamp + '.jpg', 'rb')
30 conn.storbinary('STOR ' + timestamp + '.jpg', file)
31 file.close()
32 file = open (mejiro_dir + description_file, 'rb')
33 conn.storbinary('STOR ' + description_file, file)
34 file.close()
35 conn.quit()
36 droid.makeToast('Upload completed.')
37 droid.dialogDismiss()
38 droid.vibrate()
39 else:
40 droid.makeToast('All done!')
41 droid.dialogDismiss()

The script does several things. First, it creates the mejiro directory if it doesn't already exist. Then, it generates a timestamp, opens the default camera app, and saves the taken photo in the mejiro directory. The script then prompts you to add an accompanying note, and it saves it in the same directory. Finally, it asks whether you want to upload both the photo and the note, and when you press Yes, it pushes the files to the server via the FTP protocol. Obviously, for this script to work, you need to install an FTP server on the server running Mejiro and configure the photos directory for FTP access.

The script is basically regular Python code with several SL4A facades, including dialogCreateAlert, dialogSetPositiveButtonText, dialogSetNegativeButtonText, and dialogShow. As you might have guessed, these facades are used to create a dialog with the Yes and No buttons. The dialogGetResponse facade detects the selected response, which determines the script's flow, and the makeToast facade is used to display short-duration notifications. SL4A also provides the notify facade for regular notifications, but it's not supported in QPython.

Going Further with Geofix

Another example of QPython's possibilities is the Geofix project [9] also cobbled together by yours truly. The core of Geofix is a QPython script (there is also a version for use with Python for Android) that receives the geographical coordinates of the current location and saves the obtained data in a tab-separated text file and an SQLite database. The script itself is too long to list in this article, but you can easily clone the project's GitHub repository and study it to your heart's content.

The Geofix script makes use of several facades. In the code snippet below, the startLocating and stopLocating facades are used to start and stop geolocation:

droid.startLocating()
droid.eventWaitFor('location', int(wait))
location = droid.readLocation().result
droid.stopLocating()

The eventWaitFor facade is used to pause the script until it receives geographical data, then the readLocation facade reads the obtained data. The Geofix script also uses the dialogCreateAlert and dialogGetResponse facades to create a dialog and read user response. The cameraInteractiveCapturePicture facade is used to take a snapshot with the default camera app.

In addition to the .tsv text file, the script saves the obtained geographical data in an SQLite database, and the accompanying simple web app makes it possible to view data in a browser. You can deploy the web app on a remote server and modify the upload script in Listing 2 to transfer the geofix.sqlite database to the appropriate folder on the server via FTP.

Put on a Web Facade

SL4A provides several facades for creating graphical elements like dialogs, input boxes, alerts, and so on, but, that's not all: The scripting layer also features the webViewShow facade, which makes it possible to create advanced interfaces using a mix of HTML, CSS, and JavaScript. Although this topic deserves a separate article, the code in Listing 3 gives a general idea of how the webViewShow facade works.

Listing 3

Where Am I Script

01 import time
02 import androidhelper
03 droid = androidhelper.Android()
04 droid.startLocating()
05 droid.eventWaitFor('location', 9000)
06 location = droid.readLocation().result
07 droid.stopLocating()
08 coords = location['network']
09 lat = str(coords['latitude'])
10 lon = str(coords['longitude'])
11 droid.makeToast('Network coordinates: ' + lat + ' ' + lon)
12 result = droid.geocode(lat, lon).result
13 geocode_data = droid.geocode(lat, lon)
14 country = geocode_data.result[0]['country_name']
15 city = geocode_data.result[0]['locality']
16 place = city + ', ' + country
17 f = open('/sdcard/com.hipipal.qpyplus/scripts/whereami.html', 'w')
18 f.write('<html><body><h1>Where Am I?</h1>I am in <strong>' + place + '</strong></body></html>')
19 f.close()
20 droid.webViewShow('file:///sdcard/com.hipipal.qpyplus/scripts/whereami.html')

The first part of the script obtains the geographical coordinates of the current location and performs reverse geocoding (i.e., determines the city and country name using the supplied latitude and longitude). The script then writes a simple HTML template containing the place variable to the whereami.html file. Then, the webViewShow facade opens the generated HTML page for viewing.

Final Word

Because they are lightweight and easy to master, SL4A/Python for Android and QPython provide a relatively easy way to automate tasks and build Android apps. Even if your Python skills are rather modest, you should be able to learn the Android-specific scripting basics in no time.

This article barely scratched the surface of the Android scripting layer, but I hope it gives you an idea of what you can build using either the Python for Android or QPython framework.